#ifndef _M_LOGGER_H_
#define _M_LOGGER_H_
//#define _GNU_SOURCE
#include"util.hpp"
#include"level.hpp"  
#include"formatter.hpp"
#include"sink.hpp"
#include"looper.hpp"
#include<atomic>
#include<cstdarg>
#include<unordered_map>

namespace dhylog {
    class Logger{
        public:
            using ptr=std::shared_ptr<Logger>;
            Logger(const std::string &logger_name,
                loglevel::value level,
                Formatter::ptr &formatter,
                std::vector<LogSink::ptr> sinks):
                _logger_name(logger_name),
                _limit_level(level),
                _formatter(formatter),
                _sinks(sinks.begin(),sinks.end()){}
            const std::string &name()
            {
                return _logger_name;
            }

            void debug(const std::string &file,size_t line,const std::string &fmt,...)
            {
                if(loglevel::value::DEBUG<_limit_level) {return;}
                va_list ap;
                va_start(ap,fmt);
                char *res;
                int ret=vasprintf(&res,fmt.c_str(),ap);
                if(ret==-1)
                {
                    std::cout<<"vasprintf failed!!\n";
                    return;
                }
                va_end(ap);
                serialize(loglevel::value::DEBUG,file,line,res);             
                free(res);
            }
            void info(const std::string &file,size_t line,const std::string &fmt,...)
            {
                if(loglevel::value::INFO<_limit_level) {return;}
                va_list ap;
                va_start(ap,fmt);
                char *res;
                int ret=vasprintf(&res,fmt.c_str(),ap);
                if(ret==-1)
                {
                    std::cout<<"vasprintf failed!!\n";
                    return;
                }
                va_end(ap);
                serialize(loglevel::value::INFO,file,line,res);             
                free(res);
            }
            void warn(const std::string &file,size_t line,const std::string &fmt,...)
            {
                if(loglevel::value::WARN<_limit_level) {return;}
                va_list ap;
                va_start(ap,fmt);
                char *res;
                int ret=vasprintf(&res,fmt.c_str(),ap);
                if(ret==-1)
                {
                    std::cout<<"vasprintf failed!!\n";
                    return;
                }
                va_end(ap);
                serialize(loglevel::value::WARN,file,line,res);             
                free(res);
            }
            void error(const std::string &file,size_t line,const std::string &fmt,...)
            {
                if(loglevel::value::ERROR<_limit_level) {return;}
                va_list ap;
                va_start(ap,fmt);
                char *res;
                int ret=vasprintf(&res,fmt.c_str(),ap);
                if(ret==-1)
                {
                    std::cout<<"vasprintf failed!!\n";
                    return;
                }
                va_end(ap);
                serialize(loglevel::value::ERROR,file,line,res);             
                free(res);
            }
            void fatal(const std::string &file,size_t line,const std::string &fmt,...)
            {
                if(loglevel::value::FATAL<_limit_level) {return;}
                va_list ap;
                va_start(ap,fmt);
                char *res;
                int ret=vasprintf(&res,fmt.c_str(),ap);
                if(ret==-1)
                {
                    std::cout<<"vasprintf failed!!\n";
                    return;
                }
                va_end(ap);
                serialize(loglevel::value::FATAL,file,line,res);             
                free(res);
            }
        protected:
            void serialize(loglevel::value level,const std::string &file,size_t line,char*str)
            {
                LogMsg msg(level,line,file,_logger_name,str);
                std::stringstream ss;
                _formatter->format(ss,msg);
                log(ss.str().c_str(),ss.str().size()); 

            }
            virtual void log(const char*data,size_t len)=0;
        protected:
            std::mutex _mutex;
            std::string _logger_name;
            std::atomic<loglevel::value> _limit_level;
            Formatter::ptr _formatter;
            std::vector<LogSink::ptr> _sinks;
    };
    //同步日志器
    class SyncLogger : public Logger {
        public:
        SyncLogger(const std::string &logger_name,
                loglevel::value level,
                Formatter::ptr &formatter,
                std::vector<LogSink::ptr> sinks):
                Logger(logger_name,level,formatter,sinks) {}
        protected:
            void log(const char*data,size_t len)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                if(_sinks.empty()) return;
                for(auto &sink :_sinks)
                {
                    sink->log(data,len);
                }              
            }
    };
    //异步日志器
    class AsyncLogger : public Logger 
    {
        public:
            //using ptr = std::shared_ptr<AsyncLogger>;
            AsyncLogger(const std::string &logger_name,
                loglevel::value level,
                Formatter::ptr &formatter,
                std::vector<LogSink::ptr> sinks,
                AsyncType looper_type):
                Logger(logger_name,level,formatter,sinks),
                _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::reallog,this,std::placeholders::_1),looper_type))
                {}
        protected:
            void log(const char*data,size_t len)//将数据写入缓冲区
            {
                _looper->push(data,len);
            }
            //设计一个实际落地函数将缓冲区中的数据落地
            void reallog(Buffer& buf)
            {
                if(_sinks.empty()) return;
                for(auto &sink:_sinks)
                {
                    sink->log(buf.begin(),buf.readAbleSize());
                }
  
            }
        private:
            AsyncLooper::ptr _looper;
    };


    enum class LoggerType 
    {
        LOGGER_SYNC = 0,
        LOGGER_ASYNC
    };

    class LoggerBuilder
    {
        public:
            LoggerBuilder():
                _logger_type(LoggerType::LOGGER_SYNC),
                _limit_level(loglevel::value::DEBUG),
                _looper_Type(AsyncType::ASYNC_SAFE){}
            void buildLoggerType(LoggerType type){_logger_type=type;}
            void buildEnableUnSafeAsync(){_looper_Type=AsyncType::ASYNC_UNSAFE;}
            void buildLoggerName(const std::string &name){_logger_name=name;}
            void buildLoggerLevel(loglevel::value level){_limit_level=level;}
            void buildFormatter(const std::string &pattern){
                _formatter=std::make_shared<Formatter>(pattern);
            }
            template<typename SinkType,typename ...Args>
            void buildSink(Args &&...args)
            {
                LogSink::ptr psink=SinkFactory::create<SinkType>(std::forward<Args>(args)...);
                _sinks.push_back(psink);
            }
            virtual Logger::ptr build()=0;
        protected:
            AsyncType _looper_Type;
            LoggerType _logger_type;
            std::string _logger_name;
            loglevel::value _limit_level;
            Formatter::ptr _formatter;
            std::vector<LogSink::ptr> _sinks;
    };

    class LocalLoggerBuilder :public LoggerBuilder
    {
        public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty()==false);
            if(_formatter.get()==nullptr)
            {
                _formatter=std::make_shared<Formatter>();
            }
            if(_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            if(_logger_type==LoggerType::LOGGER_ASYNC)
            {
                //std::cout<<(_looper_Type==AsyncType::ASYNC_UNSAFE);
                return std::make_shared<AsyncLogger>(_logger_name,_limit_level,_formatter,_sinks,_looper_Type);
            }
            return std::make_shared<SyncLogger>(_logger_name,_limit_level,_formatter,_sinks);
        }
    };
    class LoggerManager
    {
        public:
            static LoggerManager &getInstance()
            {
                static LoggerManager eton;
                return eton;
            }
            void addLogger(Logger::ptr &Logger)
            {
                if(hasLogger(Logger->name())) return ;
                std::unique_lock<std::mutex> lock(_mutex);
                _loggers.insert(make_pair(Logger->name(),Logger));           
            }

            bool hasLogger(std::string name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_loggers.find(name);
                if(it==_loggers.end()) return false;
                else return true;
            }
            Logger::ptr getLogger(const std::string& name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_loggers.find(name);
                if(it==_loggers.end()) return Logger::ptr();
                else return it->second;
            }
            Logger::ptr rootLogger()
            {
                return _root_logger;
            }
        private:
            LoggerManager()
            {
                std::unique_ptr<dhylog::LoggerBuilder> builder(new dhylog::LocalLoggerBuilder());
                builder->buildLoggerName("root");
                _root_logger=builder->build();
                _loggers.insert(make_pair("root",_root_logger));
            }
        private:
            std::mutex _mutex;
            Logger::ptr _root_logger;//默认日志器
            std::unordered_map<std::string,Logger::ptr> _loggers;

    };

    class GlobalLoggerBuilder :public LoggerBuilder
    {
        public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty()==false);
            if(_formatter.get()==nullptr)
            {
                _formatter=std::make_shared<Formatter>();
            }
            if(_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            Logger::ptr logger;
            if(_logger_type==LoggerType::LOGGER_ASYNC)
            {               
                logger=std::make_shared<AsyncLogger>(_logger_name,_limit_level,_formatter,_sinks,_looper_Type);
            }
            else
            {
                logger=std::make_shared<SyncLogger>(_logger_name,_limit_level,_formatter,_sinks);
            }
            LoggerManager::getInstance().addLogger(logger);
            return logger;
        }
    };
}  
#endif      